#pragma once 

#include <regex>
#include <memory>
#include <string>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/beast.hpp>
#include <utility>



namespace http{

    using method = boost::beast::http::verb;

    static auto get = boost::beast::http::verb::get;
    static auto head = boost::beast::http::verb::head;
    static auto post = boost::beast::http::verb::post;
    static auto put = boost::beast::http::verb::put;
    static auto delete_ = boost::beast::http::verb::delete_;

    class http{
        using request = boost::beast::http::request<boost::beast::http::string_body>;
        using response = boost::beast::http::response<boost::beast::http::string_body>;
    public:
        using ptr = std::shared_ptr<http>;

        http() 
            :_resolver(_io)
        {}
        ~http() {}

        std::pair<bool,std::string> make_request(
            method m, 
            const std::string& hostname, 
            const std::string& path, 
            const std::string& body = "",
            const std::string& content_type= "application/json"
        ) {
            try{
                auto& stream = connection(hostname);
                // 创建请求
                request req{m,path,11};
                req.set(boost::beast::http::field::host, hostname);
                req.set(boost::beast::http::field::user_agent, BOOST_BEAST_VERSION_STRING);

                if (m != head && m != get) {
                    req.set(boost::beast::http::field::content_type, content_type);
                    req.body() = body;
                    req.prepare_payload(); // 计算content_length
                }
                // 发起连接
                boost::beast::http::write(stream,req);
                // 等待响应
                response res;
                boost::beast::flat_buffer buff;
                boost::beast::http::read(stream, buff, res);
                if (res.result_int() >= 200 && res.result_int() < 300) {
                    return std::make_pair(true,res.body());
                }
            }catch(const std::exception& e) {
                std::cerr << "http request error : " << e.what() << std::endl;
            }
            return std::make_pair(false, "");
        }

    private:
        void unpack(const std::string& str, std::string& host,std::string& port, const std::string& default_port = "80") {
            std::regex re(R"(^([a-zA-Z0-9./-]+)(?::(\d+))?$)");
            std::smatch match;
            if (regex_match(str, match, re)) { 
                if (match[2].matched) {
                    port = match[2].str();
                } else {
                    port = default_port;
                }
                host = match[1].str();
                return ;
            }
        }
        boost::beast::tcp_stream& connection(const std::string& hostname) {
            static thread_local std::unordered_map<std::string, boost::beast::tcp_stream> streams;
            auto it = streams.find(hostname);
            if (it == streams.end()) {
                // auto stream = std::make_shared<boost::beast::tcp_stream>(_io);

                boost::beast::tcp_stream stream(_io);
                std::string host,port;
                unpack(hostname, host, port);
                auto addr = _resolver.resolve(host,port);
                stream.connect(addr);
                streams.insert({hostname,std::move(stream)});
                it = streams.find(hostname);
            }
            if (it->second.socket().is_open() ==false) {
                std::string host,port;
                unpack(hostname, host, port);
                auto addr = _resolver.resolve(host,port);
                it->second.connect(addr);
            }
            return it->second;

            // static thread_local boost::beast::tcp_stream stream(_io);
            // if (stream.socket().is_open() == false) {
            //     std::string host,port;
            //     unpack(hostname, host, port);
            //     auto addr = _resolver.resolve(host,port);
            //     stream.connect(addr);
            // }
            // return stream;
        }

    private:
        boost::asio::io_context _io;
        // boost::asio::io_context _io;

        boost::asio::ip::tcp::resolver _resolver;
    };
}  
